/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		acleditor columns.cpp
 * PURPOSE:		columns management for ACL editor dialog
 *				included as part of acleditor.cpp
 */

class AclEditorColumns : public AclEditorCommon
{
	int Width;
	int MinWidth;
	int Height;
	int LabelXes[COL_SIZE];

protected:
	AclEditorColumns(AclEditorInitParams const &Init) :
		 AclEditorCommon(Init)
	{}

	//note that columns are initialized in CreateColumns() in "acleditor init.cpp"

	virtual void Init() override
	{	
		//ensure that the scroll bar is created
		SCROLLINFO Si;

		Si.cbSize = sizeof Si;
		Si.fMask = SIF_PAGE | SIF_RANGE;
		Si.nMin = 1;
		Si.nMax = 2;
		Si.nPage = 1;

		SetScrollInfo(Wnd, SB_VERT, &Si, TRUE);

		__super::Init();
	}

	void RefreshColumnSizes()
	{
		int ListBoxY = Prefs.TopLabelHeight;

		RECT ToolbarRect;

		GetClientRect(Toolbar, &ToolbarRect);

		RaiseOnWin32ErrorT(MoveWindow(Toolbar, 0, 0, Width, ToolbarRect.bottom, TRUE));

		GetClientRect(Toolbar, &ToolbarRect);

		ListBoxY += ToolbarRect.bottom;

		MinWidth = 0;
		int visiblefraction = 0;

		//set variable-size columns to minimum
		for(UCHAR i = COL_MIN_ACE; i <= COL_MAX_VARWIDTH; i++)
			Columns[i].Width = Prefs.BitColumnWidth;

		//find total of fixed sizes
		for(UCHAR i = COL_MIN; i <= COL_MAX; i++)
			if(Columns[i].Wnd && IsWindowVisible(Columns[i].Wnd))
			{
				MinWidth += Columns[i].Width + Columns[i].PostPadWidth - 1;
				visiblefraction += Columns[i].WidthFraction;
			}


		MinWidth++;

		int visiblect = 0;
		for(UCHAR i = COL_MIN_ACE; i <= COL_MAX_VARWIDTH; i++)
			if(IsWindowVisible(Columns[i].Wnd))
				visiblect++;

		visiblect = max(visiblect, 1);

		int varspace = Width - MinWidth;
		//int varspacefraction = varspace / visiblect;

		for(UCHAR i = COL_MIN_ACE; i <= COL_MAX_VARWIDTH; i++)
			if(IsWindowVisible(Columns[i].Wnd))
			{
				Columns[i].Width += (Columns[i].WidthFraction * varspace) / visiblefraction;
				//Columns[i].Width += varspacefraction;
				//varspace -= varspacefraction; //subtract to retain any remainder
			}

		int widthct = 0;

		for(UCHAR i = COL_MIN; i <= COL_MAX; i++)
			if(Columns[i].Wnd && IsWindowVisible(Columns[i].Wnd))
			{
				RaiseOnWin32ErrorT(MoveWindow(Columns[i].Wnd, widthct, ListBoxY, Columns[i].Width, Height - ListBoxY, TRUE));
				Columns[i].LabelX = widthct;
				LabelXes[i] = Columns[i].LabelX;
				widthct += Columns[i].Width + Columns[i].PostPadWidth - 1;
			}
			else
			{
				Columns[i].LabelX = -1;
				LabelXes[i] = 0;
			}

		if(TopLabels)
		{
			RaiseOnWin32ErrorT(MoveWindow(TopLabels->Wnd, 0, ToolbarRect.bottom, Width, Prefs.TopLabelHeight, FALSE));
			RaiseOnWin32ErrorT(RedrawWindow(TopLabels->Wnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE));
		}
	}

	void SetDimensions(int Width, int Height)
	{
		this->Width = Width;
		this->Height = Height;
		RefreshColumnSizes();
		ResetScroll();
	}

	UCHAR GetColumnIndexFromWnd(HWND Wnd)
	{
		for(UCHAR i = 0; i <= COL_MAX; i++)
			if(Columns[i].Wnd == Wnd)
				return i;
		
		return MAXUCHAR;
	}

	BOOL SetColumnGroupVisibibility(ColumnGroups group, BOOL state)
	{
		BOOL NeedRefresh = FALSE;

		for(int i = COL_MIN; i <= COL_MAX; i++)
			if(Columns[i].Group == group)
			{
				NeedRefresh |= IsWindowVisible(Columns[i].Wnd) != state;
				
				ShowWindow(Columns[i].Wnd, state ? SW_SHOW : SW_HIDE);
			}

		return NeedRefresh;
	}

	int GetVisibleItems()
	{
		int ItemHeight = static_cast<int>(SendMessage(Columns[COL_MIN_ACE].Wnd, LB_GETITEMHEIGHT, 0, 0));

		RECT ToolbarRect;

		GetClientRect(Toolbar, &ToolbarRect);

		int ReservedHeight = Prefs.TopLabelHeight + ToolbarRect.bottom;

		return (Height > ReservedHeight ? Height - ReservedHeight : 0) / ItemHeight;
	}

	void ResetScroll()
	{
		SCROLLINFO Si;

		Si.cbSize = sizeof Si;
		Si.fMask = SIF_PAGE | SIF_RANGE;
		Si.nMin = 1;
		Si.nMax = (RowMax == MAXUSHORT ? 0 : RowMax) + 1;
		Si.nPage = GetVisibleItems();

		SetScrollInfo(Wnd, SB_VERT, &Si, TRUE);
		Scroll();
	}

	void Scroll()
	{
		CppCancelOneHandlerExistance CCOHE;
		try
		{
			SCROLLINFO Si;

			Si.cbSize = sizeof Si;
			Si.fMask = SIF_ALL;

			RaiseOnWin32ErrorT(GetScrollInfo(Wnd, SB_VERT, &Si)); //required requery of scroll info

			for(int i = COL_MIN_ACE; i <= COL_MAX_ACE; i++)
				if(Columns[i].Wnd)
					RaiseSendMessageListBox(Columns[i].Wnd, LB_SETTOPINDEX, Si.nPos - 1, 0);
		}
		catch(CancelOneException)
		{}
	}

	void Scroll(WPARAM wparam)
	{
		CppCancelOneHandlerExistance CCOHE;
		try
		{
			SCROLLINFO Si;

			Si.cbSize = sizeof Si;
			Si.fMask = SIF_ALL;

			RaiseOnWin32ErrorT(GetScrollInfo(Wnd, SB_VERT, &Si));

			switch (LOWORD (wparam))
			{
			case SB_TOP:
				Si.nPos = Si.nMin;
				break;

			case SB_BOTTOM:
				Si.nPos = Si.nMax;
				break;

			case SB_LINEUP:
				Si.nPos--;
				break;

			case SB_LINEDOWN:
				Si.nPos++;
				break;

			case SB_PAGEUP:
				Si.nPos -= Si.nPage;
				break;

			case SB_PAGEDOWN:
				Si.nPos += Si.nPage;
				break;

			case SB_THUMBTRACK:
				Si.nPos = HIWORD(wparam);
				break;
			}

			SetScrollInfo(Wnd, SB_VERT, &Si, TRUE);

			Scroll();
		}
		catch(CancelOneException)
		{}
	}

	virtual LRESULT RecieveMessage(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) override
	{
		switch(msg)
		{
		case WM_VSCROLL:
			Scroll(wparam);
			return 0;

		case WM_SIZE:
			SetDimensions(LOWORD(lparam), HIWORD(lparam));
			return TRUE;
		}

		return __super::RecieveMessage(wnd, msg, wparam, lparam);
	}

	void ScrollForVisible(int Pos)
	{
		SCROLLINFO Si;

		RtlZeroStruct(Si);

		Si.cbSize = sizeof Si;
		Si.fMask = SIF_ALL;

		{
			CppCancelOneHandlerExistance CCOHE;
			try
			{
				RaiseOnWin32ErrorT(GetScrollInfo(Wnd, SB_VERT, &Si));
			}
			catch(CancelOneException)
			{}
		}

		int Visible = GetVisibleItems();

		Si.nPos--;

		if(Si.nPos > Pos)
			Scroll(MAKEWPARAM(SB_THUMBTRACK, Pos + 1));
		else if((Si.nPos - 2) + Visible < Pos)
			Scroll(MAKEWPARAM(SB_THUMBTRACK, (Pos - Visible) + 2));
	}
};